package com.citrix.gs.util.beacon;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.util.EntityUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.net.URI;
import java.util.LinkedHashMap;
import java.util.List;

import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

public class BeaconHttpClient {
    
    public String getURL(){
        return uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort();
    }
    
    public URI getUri() {
        return uri;
    }

    public void setUri(URI uri) {
        this.uri = uri;
    }

    private String context;
    private URI uri;
    private CloseableHttpClient client;
    private BasicHttpContext localContext;
    
    public ObjectMapper getObjectMapper() {
        return objectMapper;
    }

    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    private ObjectMapper objectMapper;
    
    
    private ObjectMapper getDefaultMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(FAIL_ON_UNKNOWN_PROPERTIES);
        return mapper;
    }
    
    public BeaconHttpClient(URI uri, CloseableHttpClient client) {
        this.context = uri.getPath();
        
        if(!context.endsWith("/")) {
            context += "/";
        }
        this.uri = uri;
        this.client = client;
        this.localContext = null;
        this.objectMapper = getDefaultMapper();
    }

    public BeaconHttpClient(URI uri) {
        
        this(uri, HttpClientBuilder.create());
        
    }

    public BeaconHttpClient(URI uri, HttpClientBuilder builder) {
        this(uri, builder.build());
    }
    
    /**
     * Perform a GET request and parse the response and return a simple string of the content
     * @throws IOException 
     * @throws ClientProtocolException 
     * 
     * */
    
    public String get(String path) throws IOException {
        HttpGet getMethod = new HttpGet(api(path));
        HttpResponse response = client.execute(getMethod, localContext);
        
        try {
            validateResponse(response);
            return IOUtils.toString(response.getEntity().getContent());
        } finally {
            EntityUtils.consume(response.getEntity());
            releaseConnection(getMethod);
        }
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public List getList(String path) throws IOException {
        String result = get(path);
        List<LinkedHashMap<String, Object>> list = objectMapper.readValue(result, List.class);
        return list;
    }
    
    public JSONArray getJsonArray(String path) throws IOException {
        String result = get(path);
        return JSON.parseArray(result);
    }
    
    public JSONObject getJsonObject(String path) throws IOException {
        String result = get(path);
        return JSON.parseObject(result);
    }

    private void validateResponse(HttpResponse response) throws HttpResponseException {
        int status = response.getStatusLine().getStatusCode();
        if (status < 200 || status >= 400) {
            throw new HttpResponseException(status, response.getStatusLine().getReasonPhrase());
        }
    }
    
    private void releaseConnection(HttpRequestBase httpRequestBase) {
        httpRequestBase.releaseConnection();
    }
    
    private String urlJoin(String path1, String path2) {
        if (!path1.endsWith("/")) {
            path1 += "/";
        }
        if (path2.startsWith("/")) {
            path2 = path2.substring(1);
        }
        return path1 + path2;
    }
    
    private URI api(String path) {
        
        if (!path.toLowerCase().matches("https?://.*")) {
            path = urlJoin(this.context, path);
        }
        
        return uri.resolve("/").resolve(path);
    }
    
}
